'  File:		DataSetDest.cs
'  Summary:	Implementation of a managed DataSet destination component. This component sample demonstrates
'				writing a managed destination component. It adds the rows it receives in ProcessInput to a datatable
'				and if directed to do so by the SaveDataSetToXml custom property, saves the resulting data set to 
'				an xml file after execution.
'	
'  Date:		6/15/2004
'
'---------------------------------------------------------------------
'
'   This file is part of the Microsoft SQL Server Code Samples.
'   Copyright (C) Microsoft Corporation.  All rights reserved.
'
'	This source code is intended only as a supplement to Microsoft
'	Development Tools andor on-line documentation.  See these other
'	materials for detailed information regarding Microsoft code samples.
'
'	THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY ANDOR FITNESS FOR A
'	PARTICULAR PURPOSE.
'
'===================================================================== 

<System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")> _
<System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1405:ComVisibleTypeBaseTypesShouldBeComVisible")> _
<ComVisible(True), DtsPipelineComponent(DisplayName:="DataSetDestinationVB", Description:="DataSet Destination", IconResource:="Microsoft.Samples.SqlServer.Dts.DataSetDest.ico", ComponentType:=ComponentType.DestinationAdapter)> _
Public Class DataSetDestination
    Inherits PipelineComponent
    Private dataSet As DataSet
    Private runtimeDataTable As DataTable
    Private xmlDestinationFile As String
    Private Cancel As Boolean
    Private columnInfos As ColumnInfo()

    Private Structure ColumnInfo
        Public bufferIndex As Integer
        Public Name As String
    End Structure

    Public Overloads Overrides Sub ProvideComponentProperties()
        MyBase.RemoveAllInputsOutputsAndCustomProperties()
        ComponentMetaData.RuntimeConnectionCollection.RemoveAll()
        Dim dataSetVariable As IDTSCustomProperty100 = ComponentMetaData.CustomPropertyCollection.New
        dataSetVariable.Name = "RuntimeVariable"
        dataSetVariable.Description = "Name of user defined variable where the System.Data.DataSet object is placed after execution."
        Dim existingDataTableName As IDTSCustomProperty100 = ComponentMetaData.CustomPropertyCollection.New
        existingDataTableName.Name = "DataTableName"
        existingDataTableName.Value = ""
        existingDataTableName.Description = "Specifies the name given to the DataSet DataTable; or if the DataTable exists, the name of the DataTable to append to."
        Dim saveDsToDisk As IDTSCustomProperty100 = ComponentMetaData.CustomPropertyCollection.New
        saveDsToDisk.Name = "SaveDataSetToXml"
        saveDsToDisk.Value = False
        saveDsToDisk.Description = "Specifies whether the DataSet is saved to disk at the end of execution."
        Dim input As IDTSInput100 = ComponentMetaData.InputCollection.New
        input.Name = "DataSetDestinationInput"
        input.HasSideEffects = True
    End Sub

    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")> _
    Public Overloads Overrides Sub AcquireConnections(ByVal transaction As Object)
        If ComponentMetaData.RuntimeConnectionCollection.Count = 1 Then
            Dim connection As IDTSRuntimeConnection100 = ComponentMetaData.RuntimeConnectionCollection(0)
            If Not (connection.ConnectionManager Is Nothing) Then
                Dim cm As ConnectionManager = DtsConvert.ToConnectionManager(connection.ConnectionManager)
                xmlDestinationFile = CType(cm.AcquireConnection(Nothing), String)
                If xmlDestinationFile Is Nothing Then
                    Throw New Exception("The ConnectionManager " + cm.Name + " is not a FILE connection manager.")
                End If
            End If
        End If
    End Sub

    Public Overloads Overrides Sub ReleaseConnections()
        xmlDestinationFile = ""
    End Sub

    Public Overloads Overrides Sub ReinitializeMetaData()
        ComponentMetaData.RemoveInvalidInputColumns()
    End Sub

    <CLSCompliant(False)> _
    Public Overloads Overrides Function Validate() As DTSValidationStatus
        If Not (ComponentMetaData.OutputCollection.Count = 0) Then
            ComponentMetaData.FireError(0, ComponentMetaData.Name, "Should not have any ouput objects.", "", 0, Cancel)
            Return DTSValidationStatus.VS_ISCORRUPT
        End If

        If Not (ComponentMetaData.InputCollection.Count = 1) Then
            ComponentMetaData.FireError(0, ComponentMetaData.Name, "Should only have a single input.", "", 0, Cancel)
            Return DTSValidationStatus.VS_ISCORRUPT
        End If

        Dim runtimeVariable As IDTSCustomProperty100 = Me.ComponentMetaData.CustomPropertyCollection("RuntimeVariable")
        If Not (runtimeVariable.Value Is Nothing) Then
            If VariableDispenser.Contains(runtimeVariable.Value.ToString) = False Then
                ComponentMetaData.FireError(HResults.DTS_E_VARIABLENOTFOUND, ComponentMetaData.Name, ComponentMetaData.GetErrorDescription(HResults.DTS_E_VARIABLENOTFOUND), "", 0, Cancel)
                Return DTSValidationStatus.VS_ISBROKEN
            End If
            Dim variables As IDTSVariables100 = Nothing
            Me.VariableDispenser.LockOneForRead(CType(runtimeVariable.Value, String), variables)
            If Not (DtsConvert.TypeCodeFromVarType(CType(variables(0).DataType, System.UInt16)) = TypeCode.Object) Then
                ComponentMetaData.FireError(0, ComponentMetaData.Name, "The variable " + CType(runtimeVariable.Value, String) + " specified for the " + runtimeVariable.Name + " custom property is not Type object.", "", 0, Cancel)
                Return DTSValidationStatus.VS_ISBROKEN
            End If
        End If

        If ComponentMetaData.AreInputColumnsValid = False Then
            Return DTSValidationStatus.VS_NEEDSNEWMETADATA
        End If

        If CType(ComponentMetaData.CustomPropertyCollection("SaveDataSetToXml").Value, Boolean) = True AndAlso ComponentMetaData.RuntimeConnectionCollection.Count > 0 Then
            If ComponentMetaData.RuntimeConnectionCollection("XmlFile").ConnectionManager Is Nothing Then
                ComponentMetaData.FireError(0, ComponentMetaData.Name, "The SaveDataSetToXml property is true, but a connection manager has not been provided.", "", 0, Cancel)
                Return DTSValidationStatus.VS_ISBROKEN
            End If
        End If

        Return MyBase.Validate
    End Function

    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")> _
    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId:="System.Exception.#ctor(System.String)")> _
    <CLSCompliant(False)> _
    Public Overloads Overrides Function SetUsageType(ByVal inputID As Integer, ByVal virtualInput As IDTSVirtualInput100, ByVal lineageID As Integer, ByVal usageType As DTSUsageType) As IDTSInputColumn100
        If usageType = DTSUsageType.UT_READWRITE Then
            Throw New Exception("Columns must be UT_READONLY")
        End If

        Return MyBase.SetUsageType(inputID, virtualInput, lineageID, usageType)
    End Function

    Public Overloads Overrides Sub OnInputPathAttached(ByVal inputID As Integer)
        Dim input As IDTSInput100 = ComponentMetaData.InputCollection.GetObjectByID(inputID)
        Dim vInput As IDTSVirtualInput100 = input.GetVirtualInput

        For Each vCol As IDTSVirtualInputColumn100 In vInput.VirtualInputColumnCollection
            Me.SetUsageType(inputID, vInput, vCol.LineageID, DTSUsageType.UT_READONLY)
        Next
    End Sub

    <CLSCompliant(False)> _
    Public Overloads Overrides Function SetComponentProperty(ByVal propertyName As String, ByVal propertyValue As Object) As IDTSCustomProperty100
        If propertyName = "SaveDataSetToXml" AndAlso CType(propertyValue, Boolean) Then
            Dim xmlFileConnection As IDTSRuntimeConnection100 = ComponentMetaData.RuntimeConnectionCollection.New
            xmlFileConnection.Name = "XmlFile"
        Else
            If propertyName = "SaveDataSetToXml" AndAlso Not CType(propertyValue, Boolean) Then
                If ComponentMetaData.RuntimeConnectionCollection.Count > 0 Then
                    ComponentMetaData.RuntimeConnectionCollection.RemoveAll()
                End If
            End If
        End If
        Return MyBase.SetComponentProperty(propertyName, propertyValue)
    End Function

    Public Overloads Overrides Sub PreExecute()
        Me.CreateDataSet()
        Dim input As IDTSInput100 = ComponentMetaData.InputCollection(0)
        columnInfos = New ColumnInfo(input.InputColumnCollection.Count - 1) {}

        Dim col As Integer = 0
        While col < input.InputColumnCollection.Count
            Dim colInfo As ColumnInfo = New ColumnInfo
            colInfo.bufferIndex = BufferManager.FindColumnByLineageID(input.Buffer, input.InputColumnCollection(col).LineageID)
            colInfo.Name = input.InputColumnCollection(col).Name
            columnInfos(col) = colInfo
            col += 1
        End While
    End Sub

    Public Overloads Overrides Sub ProcessInput(ByVal inputID As Integer, ByVal buffer As PipelineBuffer)
        If buffer Is Nothing Then
            Throw New ArgumentNullException("buffer")
        End If

        If Not buffer.EndOfRowset Then
            While buffer.NextRow = True
                ' Dim input As IDTSInput100 = ComponentMetaData.InputCollection.GetObjectByID(inputID)
                Dim row As DataRow = runtimeDataTable.NewRow
                Dim x As Integer = 0
                While x < columnInfos.Length
                    Dim ci As ColumnInfo = CType(columnInfos(x), ColumnInfo)
                    If Not buffer.IsNull(ci.bufferIndex) Then
                        Dim bc As BufferColumn = buffer.GetColumnInfo(ci.bufferIndex)
                        If bc.DataType = DataType.DT_BYTES Then
                            row(ci.Name) = buffer.GetBytes(ci.bufferIndex)
                        Else
                            If bc.DataType = DataType.DT_IMAGE Then
                                Dim bytes(System.Convert.ToInt32(buffer.GetBlobLength(ci.bufferIndex))) As Byte
                                bytes = buffer.GetBlobData(ci.bufferIndex, 0, bytes.Length)
                                row(ci.Name) = bytes
                            Else
                                row(ci.Name) = buffer(ci.bufferIndex)
                            End If
                        End If
                    End If
                    x += 1
                End While
                runtimeDataTable.Rows.Add(row)
            End While
        End If
    End Sub

    Public Overloads Overrides Sub PostExecute()
        Dim prop As IDTSCustomProperty100 = ComponentMetaData.CustomPropertyCollection("RuntimeVariable")
        Dim vars As IDTSVariables100 = Nothing

        If Not (prop.Value Is Nothing) AndAlso VariableDispenser.Contains(CType(prop.Value, String)) Then
            VariableDispenser.LockOneForRead(CType(prop.Value, String), vars)
            vars(0).Value = Me.dataSet
        End If

        If CType(ComponentMetaData.CustomPropertyCollection("SaveDataSetToXml").Value, Boolean) = True Then
            dataSet.WriteXml(xmlDestinationFile, XmlWriteMode.WriteSchema)
        End If
    End Sub

    Private Sub CreateDataSet()
        dataSet = New DataSet(ComponentMetaData.Name)
        dataSet.Locale = System.Globalization.CultureInfo.InvariantCulture
        ' Dim input As IDTSInput100 = ComponentMetaData.InputCollection(0)
        Dim fileExists As Boolean = File.Exists(xmlDestinationFile)
        If fileExists Then
            dataSet.ReadXml(Me.xmlDestinationFile, XmlReadMode.ReadSchema)
        End If
        runtimeDataTable = GetDataTable(dataSet)
    End Sub

    Private Function GetDataTable(ByVal fromDataSet As DataSet) As DataTable
        Dim dt As DataTable = Nothing
        Dim existingDataTable As IDTSCustomProperty100 = ComponentMetaData.CustomPropertyCollection("DataTableName")

        If Not (existingDataTable.Value Is Nothing) AndAlso CType(existingDataTable.Value, String).Length > 0 Then
            If fromDataSet.Tables.Contains(CType(existingDataTable.Value, String)) Then
                dt = fromDataSet.Tables(CType(existingDataTable.Value, String))
            Else
                dt = fromDataSet.Tables.Add(CType(existingDataTable.Value, String))
            End If
        Else
            If fromDataSet.Tables.Contains(ComponentMetaData.InputCollection(0).ID.ToString(System.Globalization.CultureInfo.InvariantCulture)) Then
                dt = fromDataSet.Tables(ComponentMetaData.InputCollection(0).ID.ToString(System.Globalization.CultureInfo.InvariantCulture))
            Else
                dt = fromDataSet.Tables.Add(ComponentMetaData.InputCollection(0).ID.ToString(System.Globalization.CultureInfo.InvariantCulture))
            End If
        End If

        If dt.Columns.Count = 0 Then
            Dim input As IDTSInput100 = ComponentMetaData.InputCollection(0)
            Dim cols As Integer = 0
            While cols < input.InputColumnCollection.Count
                Dim col As IDTSInputColumn100 = input.InputColumnCollection(cols)
                Dim dc As DataColumn = dt.Columns.Add(col.Name)
                Dim isLong As Boolean = False
                Dim dataType As DataType = ConvertBufferDataTypeToFitManaged(col.DataType, isLong)
                dc.DataType = BufferTypeToDataRecordType(dataType)
                cols += 1
            End While
        End If

        Return dt
    End Function
End Class
